package de.akabahn.ebd.dbd.impl;

import java.util.HashMap;
import java.util.Map;

import de.akabahn.ebd.dbd.DBDSignal;

/**
 * Class that represents a French signal in the DBD server and that provides
 * constants and method to define and manipulate the French signal aspects.
 * 
 * @author Peter Eimann
 * @since 0.2
 * @version 1.0
 */
public class DBDSignalF extends DBDSignal {

	public static final int C = BIT8;
	public static final int S_BM = BIT27;
	public static final int S_BAL = S_BM + BIT18;
	public static final int CV = BIT26;
	public static final int AAP = BIT10;
	public static final int ASP = BIT29;
	public static final int M = BIT11;
	public static final int MC = BIT28;
	public static final int VL = BIT9;
	public static final int R = BIT9 + BIT5 + BIT24;
	public static final int RC = BIT9 + BIT4 + BIT6 + BIT24;
	public static final int RR = BIT9 + BIT1 + BIT23;
	public static final int RRC = BIT9 + BIT0 + BIT2 + BIT23;
	public static final int RR_A = BIT10 + BIT1 + BIT23;
	public static final int RRC_A = BIT10 + BIT0 + BIT2 + BIT23;
	public static final int HAGT = BIT25;

	private static Map<String, Integer> signalMap;

	static {
		signalMap = new HashMap<String, Integer>();
		signalMap.put("C", C);
		signalMap.put("S-BM", S_BM);
		signalMap.put("S-BAL", S_BAL);
		signalMap.put("CV", CV);
		signalMap.put("A-AP", AAP);
		signalMap.put("A-SP", ASP);
		signalMap.put("M", M);
		signalMap.put("MC", MC);
		signalMap.put("VL", VL);
		signalMap.put("R", R);
		signalMap.put("RC", RC);
		signalMap.put("RR", RR);
		signalMap.put("RRC", RRC);
		signalMap.put("RR-A", RR_A);
		signalMap.put("RRC-A", RRC_A);
		signalMap.put("HAGT", HAGT);
	}

	/**
	 * Default constructor
	 */
	public DBDSignalF() {
	}

	/**
	 * Initialize the signal with the signal aspects that correspond to the
	 * passed <code>intValues</code>. For convenience, the provided constants
	 * should be used.
	 * 
	 * @param intValues
	 *            The signal aspects
	 */
	public DBDSignalF(int... intValues) {
		for (int intValue : intValues) {
			this.intValue += intValue;
		}
	}

	/**
	 * Initialize the signal with the signal aspects that correspond to the
	 * passed <code>signalAspects</code>. Each aspect has to match
	 * (case-insensitive) to one of the keys in the <code>signalMap</code>,
	 * otherwise it's ignored.
	 * 
	 * @param signalAspects
	 *            The signal aspects
	 */
	public DBDSignalF(String... signalAspects) {
		for (String signalAspect : signalAspects) {
			if (signalMap.containsKey(signalAspect.toUpperCase())) {
				setBit(signalMap.get(signalAspect.toUpperCase()));
			}
		}
	}

	/**
	 * Set the signal aspects that correspond to the passed
	 * <code>signalAspects</code>. Each aspect has to match (case-insensitive)
	 * to one of the keys in the <code>signalMap</code>, otherwise it's ignored.
	 * 
	 * @param signalAspects
	 *            The signal aspects
	 */
	public void setAspects(String... signalAspects) {
		for (String signalAspect : signalAspects) {
			if (signalMap.containsKey(signalAspect.toUpperCase())) {
				setBit(signalMap.get(signalAspect.toUpperCase()));
			}
		}
	}

	/**
	 * Check if the signal shows the aspect <code>Carre C</code>
	 * 
	 * @return <code>true</code> - signal shows <code>C</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isC() {
		return ((intValue & C) == C) && ((intValue - C) == 0 || (intValue - C) == HAGT) ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Semaphore S</code> with the
	 * block system block manuel
	 * 
	 * @return <code>true</code> - signal shows <code>S</code> on BM,
	 *         <code>false</code> otherwise
	 */
	public boolean isS_BM() {
		return ((intValue & S_BM) == S_BM) && ((intValue - S_BM) == 0 || (intValue - S_BM) == HAGT) ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Semaphore S</code> with the
	 * block system block automatique lumineux
	 * 
	 * @return <code>true</code> - signal shows <code>S</code> on BAL,
	 *         <code>false</code> otherwise
	 */
	public boolean isS_BAL() {
		return ((intValue & S_BAL) == S_BAL) && ((intValue - S_BAL) == 0 || (intValue - S_BAL) == HAGT) ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Carre violet Cv</code>
	 * 
	 * @return <code>true</code> - signal shows <code>Cv</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isCv() {
		return ((intValue & CV) == CV) && ((intValue - CV) == 0 || (intValue - CV) == HAGT) ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Avertissement A</code> with
	 * permission to pass
	 * 
	 * @return <code>true</code> - signal shows <code>A</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isAAP() {
		return ((intValue & AAP) == AAP) && (intValue - AAP == 0) ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Avertissement A</code> without
	 * permission to pass
	 * 
	 * @return <code>true</code> - signal shows <code>A</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isASP() {
		return ((intValue & ASP) == ASP) && (intValue - ASP == 0) ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Feu blanc M</code>
	 * 
	 * @return <code>true</code> - signal shows <code>M</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isM() {
		return ((intValue & M) == M) && (intValue - M == 0) ? true : false;
	}

	/**
	 * Check if the signal shows the aspect
	 * <code>Feu blanc clignotant (M)</code>
	 * 
	 * @return <code>true</code> - signal shows <code>(M)</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isMC() {
		return ((intValue & MC) == MC) && (intValue - MC == 0) ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Voie libre VL</code>
	 * 
	 * @return <code>true</code> - signal shows <code>VL</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isVL() {
		return ((intValue & VL) == VL) && (intValue - VL == 0) ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Ralentissement 30 R</code>
	 * 
	 * @return <code>true</code> - signal shows <code>R</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isR() {
		return ((intValue & R) == R) && (intValue - R == 0) ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Ralentissement 60 (R)</code>
	 * 
	 * @return <code>true</code> - signal shows <code>(R)</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isRC() {
		return ((intValue & RC) == RC) && (intValue - RC == 0) ? true : false;
	}

	/**
	 * Check if the signal shows the aspect
	 * <code>Rappel de ralentissement 30 RR</code>
	 * 
	 * @return <code>true</code> - signal shows <code>RR</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isRR() {
		return ((intValue & RR) == RR) && (intValue - RR == 0) ? true : false;
	}

	/**
	 * Check if the signal shows the aspect
	 * <code>Rappel de ralentissement 60 (RR)</code>
	 * 
	 * @return <code>true</code> - signal shows <code>(RR)</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isRRC() {
		return ((intValue & RRC) == RRC) && (intValue - RRC == 0) ? true : false;
	}

	/**
	 * Check if the signal shows the aspect
	 * <code>Rappel de ralentissement 30 / avertissement RR+A</code>
	 * 
	 * @return <code>true</code> - signal shows <code>RR+A</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isRR_A() {
		return ((intValue & RR_A) == RR_A) && (intValue - RR_A == 0) ? true : false;
	}

	/**
	 * Check if the signal shows the aspect
	 * <code>Rappel de ralentissement 60 / avertissement (RR)+A</code>
	 * 
	 * @return <code>true</code> - signal shows <code>(RR)+A</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isRRC_A() {
		return ((intValue & RRC_A) == RRC_A) && (intValue - RRC_A == 0) ? true : false;
	}

	/**
	 * Check if the <code>HAGT</code> flag is set
	 * 
	 * @return <code>true</code> - the <code>HAGT</code>, flag is set
	 *         <code>false</code> otherwise
	 */
	public boolean isHAGT() {
		return (intValue & HAGT) == HAGT ? true : false;
	}

	/**
	 * Return a string representation of the current signal aspect.
	 * 
	 * @return The string representation
	 */
	public String toString() {
		StringBuilder buf = new StringBuilder();
		if (isC()) {
			buf.append("C ");
		}
		if (isS_BM()) {
			buf.append("S(BM) ");
		}
		if (isS_BAL()) {
			buf.append("S(BAL) ");
		}
		if (isCv()) {
			buf.append("Cv ");
		}
		if (isAAP()) {
			buf.append("A(aP) ");
		}
		if (isASP()) {
			buf.append("A(sP) ");
		}
		if (isM()) {
			buf.append("M ");
		}
		if (isMC()) {
			buf.append("(M) ");
		}
		if (isVL()) {
			buf.append("VL ");
		}
		if (isR()) {
			buf.append("R ");
		}
		if (isRC()) {
			buf.append("(R) ");
		}
		if (isRR()) {
			buf.append("RR ");
		}
		if (isRRC()) {
			buf.append("(RR) ");
		}
		if (isRR_A()) {
			buf.append("RR+A ");
		}
		if (isRRC_A()) {
			buf.append("(RR)+A ");
		}
		if (isHAGT()) {
			buf.append("HAGT ");
		}

		return buf.toString();
	}
}
